package br.com.upis.sispat.dao.impl;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.SQLException;
import java.util.List;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import br.com.upis.sispat.dao.Dao;
import br.com.upis.sispat.exception.PersistentException;
import br.com.upis.sispat.util.HibernateUtil;
@Scope("singleton")
@Repository
public class GenericDaoImpl<T extends Serializable> implements Dao<T> {

	private SessionFactory sessionFactory;
	private static final long serialVersionUID = 7129219004826849502L;
	private final Logger logger = Logger.getLogger(this.getClass());
	public GenericDaoImpl() {
		this.sessionFactory = HibernateUtil.getSessionFactory();
		BasicConfigurator.configure();
		logger.info("Iniciando a aplicacao");
	}
	@Transactional
	public void create(T object) throws SQLException {
		Transaction tx = getSession().getTransaction();
		try {

			tx.begin();

			getSession().save(object);
			getSession().flush();

			tx.commit();

		} catch (Exception e) {
			tx.rollback();
			throw new PersistentException(e);
		}

	}

	@Transactional
	public void update(T object) throws SQLException {
		Transaction tx = getSession().getTransaction();
		try {

			tx.begin();

			getSession().merge(object);
			getSession().flush();

			tx.commit();
		} catch (Exception e) {
			tx.rollback();
			throw new PersistentException(e);
		}

	}

	@Transactional
	public void delete(T object) throws SQLException {
		Transaction tx = getSession().getTransaction();
		try {

			tx.begin();

			getSession().delete(object);
			getSession().flush();

			tx.commit();
		} catch (Exception e) {
			tx.rollback();
			throw new PersistentException(e);
		}

	}

	public void refresh(T object) throws SQLException {
		Transaction tx = getSession().getTransaction();
		try {

			tx.begin();

			getSession().refresh(object);
			getSession().flush();

			tx.commit();
		} catch (Exception e) {
			tx.rollback();
			throw new PersistentException(e);
		}
	}

	@SuppressWarnings("unchecked")
	public List<T> list(String hqlQuery, String... parameters) {

		List<T> resultList = null;
		Transaction tx = getSession().getTransaction();
		try {

			tx.begin();

			Query query = getQueryResult(hqlQuery, parameters);

			resultList = query.list();

			tx.commit();
		} catch (HibernateException e) {
			tx.rollback();
		}

		return resultList;
	}

	public Session getSession() {
		return sessionFactory.getCurrentSession();
	}
	@SuppressWarnings("unchecked")
	public T getSingleObject(String hqlQuery, String... parameters) {

		T object = null;

		Transaction tx = getSession().getTransaction();

		try {
			tx.begin();

			Query query = getQueryResult(hqlQuery, parameters);

			List<T> resultList = query.list();

			tx.commit();

			if (!resultList.isEmpty()) {

				object = resultList.get(0);
			}
		} catch (HibernateException e) {
			tx.rollback();
		}

		return object;
	}
	@SuppressWarnings("unchecked")
	public T getObjectFromId(Class<T> clazz, Serializable id) {

		T object = null;

		Transaction tx = getSession().getTransaction();

		try {
			tx.begin();

			object = (T) getSession().get(clazz, id);

			tx.commit();
		} catch (Exception e) {
			tx.rollback();
		}

		return object;
	}

	private Query getQueryResult(String hqlQuery, String... parameters) {
		Query query = getSession().createQuery(hqlQuery);

		int i = 0;

		for (String param : parameters) {

			query.setString(i, param);

			i++;

		}
		return query;
	}

	public Dao<T> getDao() {

		return new GenericDaoImpl<T>();
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> listAll(Class<T> clazz) {

		Transaction tx = getSession().getTransaction();

		List<T> list = null;
		try {
			tx.begin();

			list = getSession().createCriteria(clazz).list();

			tx.commit();
		} catch (HibernateException e) {
			tx.rollback();
		}

		return list;
	}

	@SuppressWarnings("unchecked")
	@Override
	public T getSingleObject(String hqlQuery) {

		T object = null;

		Transaction tx = getSession().getTransaction();

		try {
			tx.begin();

			Query query = getSession().createQuery(hqlQuery);

			List<T> resultList = query.list();

			tx.commit();

			if (!resultList.isEmpty()) {

				object = resultList.get(0);
			}
		} catch (HibernateException e) {
			tx.rollback();
		}

		return object;
	}

	@SuppressWarnings("unchecked")
	public List<T> listByCriteriaDemanda(Integer startPage, Integer maxPage,
			List<Criterion> restricoes, List<Projection> projecoes,
			List<Order> ordenacao, Class<T> modelClass)
			throws IllegalArgumentException, IllegalAccessException,
			InvocationTargetException {

		Transaction tx = getSession().getTransaction();

		tx.begin();
		Criteria criteria = getSession().createCriteria(modelClass);

		if (restricoes != null) {
			for (Criterion r : restricoes) {
				criteria.add(r);
			}
		}
		if (projecoes != null) {
			for (Projection p : projecoes) {
				criteria.setProjection(p);
			}
		}
		if (ordenacao != null) {
			for (Order order : ordenacao) {
				criteria.addOrder(order);
			}
		}
		criteria.setFirstResult(startPage);
		criteria.setMaxResults(maxPage);
		List<T> lista = criteria.list();

		for (T t : lista) {
			for (Method method : t.getClass().getMethods()) {
				if (method.getName().indexOf("get") > -1) {
					Object obj = method.invoke(t);
					if (obj instanceof java.util.Collection) {
						for (T lista1 : (List<T>) obj) {
							Hibernate.initialize(lista1);
						}
					}
				}
			}
		}
		tx.commit();
		return lista;
	}

	public Integer count(Class<T> modelClass) {
		Integer size = 0;

		Transaction tx = getSession().getTransaction();

		tx.begin();
		Criteria criteria = getSession().createCriteria(modelClass);
		criteria.setProjection(Projections.rowCount());

		size = Integer.parseInt(criteria.list().get(0).toString());

		tx.commit();

		return size;
	}

	@Override
	public void saveOrUpdate(T o) throws PersistentException {

		Transaction tx = getSession().getTransaction();
		try {

			tx.begin();

			getSession().saveOrUpdate(o);
			getSession().flush();

			tx.commit();
		} catch (Exception e) {
			tx.rollback();
			throw new PersistentException(e);
		}
		
	}
}
